<template>
  <div class="vxeTableX">
    <vxe-table :data="tableDataD" class="myvxeTable haveScroll1"
               :class="{isTree: isTree,stripe: props.stripe,columnC: columnConfig.isCurrent,columnH:columnConfig.isHover}"
               :row-config="rowConfig" :id="idKey"
               :tree-config="isTree?treeProps:undefined"
               :stripe="props.stripe" :size="props.size"
               height="100%" :column-config="columnConfig"
               :scroll-y="scrollY" :scroll-x="scrollX"
               :checkbox-config="checkboxConfig"
               max-height="100%" border v-bind="$attrs">

      <vxe-column v-for="item in tableField"
                  :key="`_${item.label}_${item.prop}_${item.slot}`"
                  v-bind="item">


      </vxe-column>


    </vxe-table>
  </div>
</template>

<script setup>
import {ref, watch, nextTick, computed, useAttrs} from 'vue'
defineOptions({name: "vxeTableX"})


const props = defineProps({

  // 当前勾选的列表。仅当出现选项时
  modelValue: {
    type: Object,
    default: []
  },

  // 表单数据
  tableData: {
    type: Object,
    default: []
  },

  // 表单标题字段
  tableTitle: {
    type: Object,
    // slot：自定义插槽名字，数据列表行的自定义内容，里面包含了element组件的三个参数值。
    // show: 是否显示当前列，非false的值都默认显示。
    // callBack: 显示内容前的回调函数。callBack(当前row对象)。返回一个新的显示内容。
    // html: 已html的形式渲染，如：{html: (row,column,$index)=>'<span style="color:red;">12</span>'}。
    // rowClick: 点击单元格是否会触发点击行事件。如果是插槽需要配置slotClick，其他默认是需要。为false时插槽也不会触发事件
    // slotClick: 如果是插槽默认是不需要，true时是触发点击事件。
    // rowSelect: 点击单元格是否会触发行勾选改变（配合selectChange）。如果是插槽默认是不需要，其他默认是需要。
    // noClick: 是否启动自定义勾选,
    // help: string，开启说明提示文本。
    // helpOptions: {}，提示组件的属性。
    default: [
      {label: '序号', prop: 'title', isInput: 'name'},
      {label: '查阅状态', prop: 'phone', slot: 'cy', type: 'index', sortable: true}]
  },


  // 唯一key。为了防止后端返回的id不是唯一，可以设置多个字段值拼接，如：['id','code'] => `${id}_${code}`
  rowKey: {
    type: [Object, String],
    default: 'id'
  },

  // 表格大小样式
  size: {
    type: String,
    default: 'small'
  },


  // region 滚动条样式

  // 滚动条设置
  scrollStyle: {
    type: Object,
    default: {
      // always: true, // 可以显示滚动条时，总是显示滚动条
      // width: '14px', // 两条滚动条宽带。接受Number或string
      // widthX: '14px', // 横向滚动条宽带。下面值都有总体和XY区分（XY权限优先）
      // color: '#909399',  // 两条滚动条颜色
      // opacity: 0.7, // 滚动条透明度
      // translate: '2px', // 偏移量
    }
  },

  // endregion


  // region 滚动条Y
  scrollY: {
    type: Object,
    default: {}
  },
  // endregion


  // region 树形结构

  // 树形结构
  treeProps: {
    type: Object,
    default: {}
  },

  // 树形结构图标
  treeIcon: {
    type: String,
    default: 'CaretRight'
  },

  // endregion


  // region 行配置

  // 斑马线
  stripe: {
    type: Boolean,
    default: true
  },

  // 是否高亮
  highlightCurrentRow: {
    type: Boolean,
    default: true
  },

  rowConfig: {
    type: Object,
    default: {}
  },


  // endregion


  // region 列配置
  columnConfig: {
    type: Object,
    default: {}
  },

  // endregion

  // region 复选框

  noClick: {
    type: Boolean,
    default: false
  },

  checkboxConfig: {
    type: Object,
    default: {}
  },
  // endregion

})

// region 工具

// 获取id
function getMid(len = 30) {
  let result = Date.now().toString();
  while (result.length < len) {
    let temp = Math.random().toString(36)
    if (temp.length > 2) {
      temp = temp.slice(2, temp.length)
    } else {
      temp = "xhyk"
    }
    result = `${temp}${result}`
  }

  if (result.length > len) {
    let index = result.length - len
    result = result.slice(index, result.length)
  }

  return result
}

// 判断prop传参的值。接受[String,Number]的style样式尺寸
function getPx(text, defaultPx = 'px') {
  let tempList = ['px', 'rpx', 'vw', 'vh', '%', 'em', 'auto']
  if (typeof text == 'string') {
    const havePx = tempList.find(row => text.includes(row))
    if (havePx) {
      return text
    } else {
      return `${parseFloat(text)}${defaultPx}`
    }
  } else if (typeof text == 'number') {
    let temp = parseFloat(text)
    return `${temp}${defaultPx}`
  }
  return text
}


// 遍历嵌套取值。根据children字段遍历下级列表。返回当前子级对象
async function findChildren(data = [], callBack = undefined, children = "children") {
  let tempResult = undefined
  const findFn = async (ChildData, father) => {
    let isOk = false
    for (let item of ChildData) {
      if (typeof callBack == "function") {
        const isBreak = await callBack(item, father)
        if (isBreak == true) {
          tempResult = item
          isOk = true
          break;
        }
      }
      if (isOk) {
        tempResult = item
        return item
      }

      // 如果有下一级数据
      if (item[children] != undefined && item[children].length > 0) {
        let result = await findFn(item[children], item)
        if (result) {
          return true
        }
      }
    }
    return isOk;
  }
  await findFn(data, {})
  return tempResult
}


// region 防抖/节流。在xhykApi里
class MyTimeOut {

  // oneTime：节流延迟，只有第一次有效，常用滚动分页。lastTime：防抖延迟，只有最后一次有效，常用搜索框
  constructor(oneTime = 500, lastTime = 500) {
    this.oneTime = oneTime
    this.lastTime = lastTime

    this.oneId = undefined
    this.lastId = undefined
  }

  // 节流执行。只执行第一次
  runOne(callBack) {
    if (this.oneId) {
      return;
    }
    this.oneId = setTimeout(async () => {
      await callBack()
      this.oneId = undefined
    }, this.oneTime)
  }

  // 防抖执行。只执行最后一次
  runLast(callBack) {
    clearTimeout(this.lastId)
    this.lastId = setTimeout(async () => {
      await callBack()
      this.lastId = undefined
    }, this.lastTime)
  }

  // 死循环等待，直到breakCallBack为true
  async runSwitch(callBack, breakCallBack) {
    while (true) {
      const result = await breakCallBack()
      if (result) {
        break
      }
    }
    callBack()
  }
}

// endregion

// region 双向更新props值

class WatchField {

  // 初始化。
  constructor(emit, props) {
    // region 初始化
    /**
     * const props = defineProps({
     modelValue: {
     type: [String,Number,Object],
     default: ''
     }
     })
     const emit = defineEmits(['update:modelValue','save']);
     const watchObj = new WatchField(emit,props);
     const number = ref(233);
     watchObj.watchModel(number);
     * */
    // endregion
    this.props = props
    this.emit = emit
  }


  // 双向绑定modelValue。const number = ref('233');watchObj.watchModel(number)
  watchModel(obj, callBackObj, callBackModel, options = {}) {
    let other = {immediate: true, deep: true}
    Object.assign(other, options)
    watch(() => this.props.modelValue, (newV, oldV) => {
      if (typeof callBackModel == 'function') {
        callBackModel(newV, oldV)
      }
      obj.value = this.props.modelValue
    }, other)

    watch(obj, (query, query2) => {
      if (typeof callBackObj == 'function') {
        callBackObj(query, query2)
      }
      this.emit('update:modelValue', query)
    }, other)

  }


  // 防抖监听。const number = ref(233);watchObj.watchLast(number,undefined,{number:2})
  // options:{ immediate: '立刻执行',deep: '深度监听',lastTime: '防抖间隔',number: '监听上限次数' }
  watchLast(obj, callBack, options) {
    let other = {immediate: true, deep: true}
    Object.assign(other, options)
    // 防抖
    const thisTime = new MyTimeOut(other.oneTime, other.lastTime || 100)

    // 执行上限次数。包含第一次
    let number = 1

    watch(obj, (query, query2) => {
      thisTime.runLast(async () => {
        if (other.number != undefined && number > other.number) {
          return;
        }
        number += 1
        if (typeof callBack == 'function') {
          await callBack(query, query2)
        }
      })
    }, other)
  }

  // 监听并发送事件
  watchEmit(obj, emitStr, callBack, options) {
    this.watchLast(obj, (newV, oldV) => {
      if (typeof callBack == 'function') {
        callBack(newV, oldV)
      }
      this.emit(emitStr, newV)
    }, options)
  }

  // 双向绑定其他字段，propsKey：props里面哪个对象的名称。const number = ref('233');watchObj.watchModel(number,'number')
  watchModelEmit(obj, propsKey, callBackObj, callBackModel, options = {}) {
    let other = {immediate: true, deep: true}
    Object.assign(other, options)
    watch(() => this.props[propsKey], (newV, oldV) => {
      if (typeof callBackModel == 'function') {
        callBackModel(newV, oldV)
      }
      obj.value = this.props[propsKey]
    }, other)

    watch(obj, (query, query2) => {
      if (typeof callBackObj == 'function') {
        callBackObj(query, query2)
      }
      this.emit(`update:${propsKey}`, query)
    }, other)
  }

}

// endregion

// endregion

const emit = defineEmits(['update:modelValue'])

const watchObj = new WatchField(emit, props)


// region 树形结构配置

// 是否为树形结构
const isTree = ref(false)

// 是否为树形结构数据
const treeProps = computed(() => {
  let result = {
    childrenField: 'children',
    transform: false,
    showLine: true,
    rowField: idKey.value,
    parentField: 'parentId'
  }
  if (props.treeProps) {
    Object.assign(result, props.treeProps)
  }
  if (props.treeProps.children) {
    result.childrenField = props.treeProps.children
  }

  return result
})

// endregion


// region 处理数据

const idKey = ref('tableV3Key')

// 设置唯一key对应的值
const setKeyValue = (item) => {
  let idList = []
  if (typeof props.rowKey == 'string') {
    idList = [props.rowKey]
  } else {
    // ['id','code'] => `${id}_${code}`
    idList = props.rowKey
  }

  let idValue = ""
  idList.forEach(key => {
    idValue = `${idValue}_${item[key]}`
  })
  item[idKey.value] = idValue

  return idValue
}

// 表格的id的值列表
const idKeyValueList = ref([])

// 表格数据
const tableDataD = ref([])

// 表格的一层数据
const levelData = ref([])

// 是否可以进行监听modelValue变化
const canWatchModelValue = ref(false)

watchObj.watchLast(() => props.tableData, (newV) => {
  if (!canWatchModelValue.value && tableDataD.value.length > 0) {
    return
  }

  nextTick(async () => {
    isTree.value = false
    idKeyValueList.value = []
    canWatchModelValue.value = false

    await findChildren(newV, (child, father = {}) => {
      let keyValue = setKeyValue(child)
      idKeyValueList.value.push(keyValue)
      child[treeProps.value.parentField] = father[idKey.value]
      levelData.value.push(child)

      if (Object.keys(father).length > 0) {
        isTree.value = true
      }
    }, treeProps.value.childrenField || 'children')


    tableDataD.value = newV
    console.log(234, tableDataD.value.length)
    canWatchModelValue.value = true
  })
})

// endregion


// region 表格字段

const tableField = ref([])

// 处理字段方法
const setFieldFn = () => {
  let result = []

  props.tableTitle.forEach(item => {

    item.align = item.align == undefined ? 'center' : item.align

    item.field = item.prop

    item.title = item.label


    if (item.type == 'index') {
      item.type = 'seq'
    }

    if (item.type == 'selection' || item.isSelection) {
      item.type = 'checkbox'
      item.field = ''
    }

    if (item.i18Fn == undefined) {
      item.i18Fn = props.i18Fn
    }



    if (typeof item.fixed != 'string') {
      item.fixed = undefined
    }

    item.visible = item.show

    result.push(item)
  })

  tableField.value = result
}

watchObj.watchLast(() => props.tableTitle, (newV) => {
  setFieldFn()
})


// endregion


// region 行配置

const rowConfig = computed(() => {
  const result = {
    useKey: true,
    isHover: props.highlightCurrentRow,
    keyField: idKey.value,
    isCurrent: true,
  }

  Object.assign(result, props.rowConfig)

  return result
})

// endregion


// region 列配置

const columnConfig = computed(() => {
  const result = {
    isHover: false,
    resizable: true,
    isCurrent: false,
  }
  Object.assign(result, props.columnConfig)
  return result
})

// endregion


// region 滚动条Y

const scrollY = computed(() => {
  const result = {
    enable: true,
    gt: 0,
    mode: 'wheel'
  }
  Object.assign(result, props.scrollY)
  return result
})

// endregion


// region 滚动条X
const scrollX = computed(() => {
  const result = {
    enable: true,
    gt: 0,
  }
  Object.assign(result, props.scrollX)
  return result
})
// endregion


// region 滚动条样式

const scrollWidthX = ref('')
const scrollWidthY = ref('')
const scrollTranslateX = ref('')
const scrollTranslateY = ref('')
const scrollColorX = ref('')
const scrollColorY = ref('')
const scrollDisplayX = ref('')
const scrollDisplayY = ref('')
const scrollOpacityX = ref(0.7)
const scrollOpacityY = ref(0.7)
const scrollAlways = ref(false)

watchObj.watchLast(() => props.scrollStyle, (newV) => {
  const result = {
    width: '10px',
    translate: '2px',
    color: '#909399',
    opacity: 0.7,
    always: true,
  }

  Object.assign(result, props.scrollStyle)


  scrollWidthX.value = getPx(result.widthX || result.width)
  scrollWidthY.value = getPx(result.widthY || result.width)

  scrollTranslateX.value = getPx(result.translateX || result.translate)
  scrollTranslateY.value = getPx(result.translateY || result.translate)

  scrollColorX.value = result.colorX || result.color
  scrollColorY.value = result.colorY || result.color

  scrollDisplayX.value = result.showX || result.show ? 'block' : 'none';
  scrollDisplayY.value = result.showY || result.show ? 'block' : 'none';

  scrollOpacityX.value = result.opacityX || result.opacity;
  scrollOpacityY.value = result.opacityY || result.opacity;
  scrollAlways.value = result.always;
})

// endregion


// region 复选框

const checkboxConfig = computed(() => {
  const result = {
    labelField: 'dataSelection'
  }
  Object.assign(result, props.checkboxConfig)
  if (props.noClick){
    result.checkStrictly = true
  }
  return result
})

// endregion


</script>

<style scoped lang='scss'>
.vxeTableX {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  overflow: hidden;
}

.myvxeTable {
  height: 100%;
  width: 100%;
}


// region 滚动条样式

@mixin scrollStyleValue {
  ::-webkit-scrollbar {
    width: v-bind(scrollWidthX);
    height: v-bind(scrollWidthY);
    cursor: pointer;
  }


  ::-webkit-scrollbar-thumb {
    border-radius: 4px;
  }

  ::-webkit-scrollbar-track,
  ::-webkit-scrollbar-corner {
    background-color: transparent;
  }

  ::-webkit-scrollbar-thumb {
    background-color: v-bind(scrollColorX);
  }
}

.scrollStyle {
  .haveScroll {
    @include scrollStyleValue();
  }

  .noScroll {
    ::-webkit-scrollbar {
      display: none;
    }

    &:hover {
      @include scrollStyleValue();
    }
  }
}


/** 默认模式 */
[data-vxe-ui-theme="light"] {
  @extend .scrollStyle;
}

/** 暗黑模式 */
[data-vxe-ui-theme="dark"] {
  @extend .scrollStyle;
}

// endregion


// region 斑马背景色

.isTree.stripe {
  :deep(.vxe-body--row) {
    &:nth-child(2n + 1) {
      background-color: white;
    }

    &:nth-child(2n) {
      background-color: #FAFAFA;
    }
  }
}

// endregion


// region 行样式
:deep(.vxe-body--row.row--current) {
  background-color: #EDF2FA !important;
  border-bottom: 1px solid #E8EAEC !important;
  color: var(--menuSysColor, #606266);
}

// endregion


// region 列样式
:deep(.columnC) {
  .col--current {
    background-color: #EDF2FA !important;
    border-bottom: 1px solid #E8EAEC !important;
  }
}

:deep(.columnH) {
  .vxe-header--column {
    &:hover {
      background-color: #EDF2FA !important;
    }
  }
}

// endregion

</style>